《Windows Internals》第三章_陷阱分发

3.1:陷阱分发

第三部分:系统服务分发

系统服务分发

         windows用于系统服务分发的指令取决于其执行时所在的处理器。

  • 在PentiumII之前的处理器,windows使用int 0x2E陷阱指令,使windows填充IDT(中断描述符表Interrupt Descriptor Table)中的2E表项,使其指向系统服务分发器。该陷阱导致线程由用户模式转换到内核模式,并且进入系统服务分发器。EAX指明了所请求的系统服务号,EDX指向的是调用者传递给系统服务的参数表
  • 在PentiumII以后的处理器,使用的是sysenter指令,windows在引导的时候将内核服务分发器例程的地址保存在MSR(64位CPU寄存器,可以利用rdmsr或者wdmsr来读写)中,执行该指令后续操作和上面一致。最后利用sysexit返回用户模式。
  • X64使用syscall指令,将系统调用号保存在eax中,前四个参数放在寄存器(rcx/rdx/r8/r9)中,剩下的参数在栈中
  • IA64使用epc指令

内核模式下的系统分发

         在windows内核模式下,CPU不需要进行中断或者syscall了,因为此时CPU已经运行在特权模式下了,只需要像普通的例程调用一样就好,问题是对于NT的函数大多数没有被导出,无法直接调用。但如果直接像调用API一般直接调用NtOpenProcess之类的系统服务函数时,内核保存的原先模式值仍然是用户模式(进内核之前当然是用户模式咯~),但又检测到传递来的地址是一个内核模式地址(因为在当前内核模式下调用),于是会导致调用失败(STATUS_ACCESS_VIOLATION)。必须使用ZT函数因为他们已经被文档化了。请见博客:https://findream.github.io/2018/01/23/%E5%86%85%E6%A0%B8Nt%E5%92%8CZw%E5%87%BD%E6%95%B0%E5%8C%BA%E5%88%AB/
         子系统DLL调用Ntdll.dll中函数(系统调用根本执行的函数)来实现已经文档化的函数,但是windows USER和GDI函数例外,因为这些函数根本不涉及Ntdll.dll,系统分发指令在user32.dll或者GDI.dll中实现。

  • windows内核API调用
    • 1)windows应用程序调用Fun()
    • 2) 调用kernelbase.dll中的Fun(),这是实现调用的核心所在
    • 3)调用Ntdll.dll中的Fun(),这个Fun去引发系统服务的陷阱,进入内核模式并且把Fun的系统服务号传递给系统服务分发器
    • 4)系统服务分发器(Ntoskrnl.exe:KiSystemService())调用Fun执行
  • USER或者GDI函数
    • 1)应用程序中调用USER或者GDI函数
    • 2)调用User32.dll或者GDI.dll的Fun(),通过systementer进入内核模式,
    • 3)调用Ntoskrnl.exe的KiSystemService指向Win32k.sys中的服务入口点。
    • 4)执行服务,并返回

Ring3 —> Ring0 的系统调用:
Kernel32.dll(API)—>ntdll.dll(Nt/Zw)—>用户模式转内核模式—>Ntoskrnl.exe(NT)—>完成I/O请求(原路返回)
Ring0 —> Ring0 的系统调用:
Ntoskrnl.exe(Zw)—>Ntoskrnl.exe(Nt)
参考文献:
windows internals
https://www.cnblogs.com/uAreKongqi/p/6597701.html